package com.situ.b2c.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.dbcp2.BasicDataSource;

public class JDBCUtil {

	// private static final String DIVER_NAME = "com.mysql.cj.jdbc.Driver";
	// private static final String USER_NAME = "root";
	// private static final String USER_PASS = "root";
	// private static final String DB_URL =
	// "jdbc:mysql://localhost:3306/JAVA2013?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=true&allowMultiQueries=true";
	// 定义一个 静态的连接池的数据源
	private static BasicDataSource dataSource;
	private static final char UNDER_LINE = '_';
	private static final String DIVER_NAME;
	private static final String USER_NAME;
	private static final String USER_PASS;
	private static final String JDBC_URL;
	static {
		// 定义一个空的Porperites实例
		Properties properties = new Properties();
		// 根据当前类得到其Class实例
		Class<?> clazz = JDBCUtil.class;
		// 得到类加载对象
		ClassLoader classLoader = clazz.getClassLoader();
		// 通过类加载对象得到资源的路径，并包装成File对象。
		File file = new File(classLoader.getResource("jdbc.properties").getPath());
		try {
			InputStream inputStream = new FileInputStream(file);
			// load方法将jdbc.properties文件读取到properties对象中。
			properties.load(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
		DIVER_NAME = properties.getProperty("db.driverClass");
		USER_NAME = properties.getProperty("db.username");
		USER_PASS = properties.getProperty("db.password");
		JDBC_URL = properties.getProperty("db.jdbcUrl");
		//
		dataSource = new BasicDataSource();
		// 向数据源中设置数据库的链接
		dataSource.setDriverClassName(DIVER_NAME);
		dataSource.setUsername(USER_NAME);
		dataSource.setPassword(USER_PASS);
		dataSource.setUrl(JDBC_URL);
		// 配置连接池特有的信息
		dataSource.setInitialSize(10);// 初始化连载数
		dataSource.setMaxIdle(15); // 最大链接数
		dataSource.setMinIdle(8);// 最小链接数
		dataSource.setMaxWaitMillis(1500);// 最大的等待毫秒数。
	}

	/**
	 * # 查询SQL语句得到一个实例
	 * 
	 * @param <T>
	 * @param clazz
	 * @param sql
	 * @param args
	 * @return
	 */
	public static <T> T executeQuery4One(Class<T> clazz, String sql, Object... args) {
		//准备返回的实例
		T object = null;
		// 得到数据库的链接
		Connection connection = getConnection();
		if (connection != null) {
			// 预编译的Statement的对象
			PreparedStatement statement = null;
			// 结果集对象
			ResultSet resultSet = null;
			try {
				// 通过数据库链接的prepareStatement完成SQL语句的预编译
				statement = connection.prepareStatement(sql);
				// 判断传入的可变参数有数据
				if (args != null && args.length > 0) {
					// 通过遍历的方式对SQL语句中的占位符进行赋值。
					int parameterIndex = 1;
					for (Object arg : args) {
						statement.setObject(parameterIndex, arg);
						parameterIndex++;
					}
				}
				// 得到结果集
				resultSet = statement.executeQuery();
				// 通过clazz对象得到类中所有的属性的集合
				// Field[] fieldArray = clazz.getDeclaredFields();
				// 得到类中所有的属性集合，包括本类及父类，不包括 java.lang.Object父类
				List<Field> fieldList = buildAllField(clazz);
				// 遍历结果集
				while (resultSet.next()) {
					try {
						// 通过newInstance 得到类的实例
						object = clazz.newInstance();
						// 遍历类中属性的数据
						for (Field field : fieldList) {
							// 判断当前的属性为private的唯一。
							if (field.getModifiers() == Modifier.PRIVATE
									|| field.getModifiers() == Modifier.PROTECTED) {
								// 得到属性的数据类型
								Class<?> type = field.getType();
								// 得到属性的名称
								String fieldName = field.getName();
								// 根据类的属性名称得到其对应的表中字段名称 rowId---> ROW_ID
								String columnName = field2Column(fieldName);
								Object colunmObj = null;
								// 判断当前的属性的数据类型为java.util.Date
								if (type.getTypeName().equals("java.util.Date")) {
									colunmObj = resultSet.getDate(columnName);
								} else {
									// 根据得到的字段名称从结果集中将对应的值解析出来
									colunmObj = resultSet.getObject(columnName);
								}
								if (colunmObj != null) {
									// 根据类的属性名称得到其对应的setter方法名称 rowId ---> setRowId
									String setterMethodName = buildSetterMethodName(fieldName);
									try {
										// 根据setter方法名称得到Method的实例
										Method setterMethod = clazz.getMethod(setterMethodName, type);
										try {
											// 让方法自运行
											setterMethod.invoke(object, colunmObj);
										} catch (IllegalArgumentException | InvocationTargetException e) {
											e.printStackTrace();
										}
									} catch (NoSuchMethodException | SecurityException e) {
										e.printStackTrace();
									}
								}
							}
						}
					} catch (InstantiationException | IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {// 关闭资源
				try {
					if (resultSet != null) {
						resultSet.close();
					}
					if (statement != null) {
						statement.close();
					}
					if (connection != null) {
						connection.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}

		return object;
	}

	/**
	 * #执行select的SQL语句
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	public static <T> List<T> executeQuery(Class<T> clazz, String sql, Object... args) {
		// 定义一个空的集合，用于保持通过查询得到数据
		List<T> list = new ArrayList<T>();
		// 得到数据库的链接
		Connection connection = getConnection();
		if (connection != null) {
			// 预编译的Statement的对象
			PreparedStatement statement = null;
			// 结果集对象
			ResultSet resultSet = null;
			try {
				// 通过数据库链接的prepareStatement完成SQL语句的预编译
				statement = connection.prepareStatement(sql);
				// 判断传入的可变参数有数据
				if (args != null && args.length > 0) {
					// 通过遍历的方式对SQL语句中的占位符进行赋值。
					int parameterIndex = 1;
					for (Object arg : args) {
						statement.setObject(parameterIndex, arg);
						parameterIndex++;
					}
				}
				// 得到结果集
				resultSet = statement.executeQuery();
				// 通过clazz对象得到类中所有的属性的集合
				// Field[] fieldArray = clazz.getDeclaredFields();
				// 得到类中所有的属性集合，包括本类及父类，不包括 java.lang.Object父类
				List<Field> fieldList = buildAllField(clazz);
				// 遍历结果集
				while (resultSet.next()) {
					try {
						// 通过newInstance 得到类的实例
						T object = clazz.newInstance();
						// 遍历类中属性的数据
						for (Field field : fieldList) {
							// 判断当前的属性为private的唯一。
							if (field.getModifiers() == Modifier.PRIVATE
									|| field.getModifiers() == Modifier.PROTECTED) {
								// 得到属性的数据类型
								Class<?> type = field.getType();
								// 得到属性的名称
								String fieldName = field.getName();
								// 根据类的属性名称得到其对应的表中字段名称 rowId---> ROW_ID
								String columnName = field2Column(fieldName);
								Object colunmObj = null;
								// 判断当前的属性的数据类型为java.util.Date
								if (type.getTypeName().equals("java.util.Date")) {
									colunmObj = resultSet.getDate(columnName);
								} else {
									// 根据得到的字段名称从结果集中将对应的值解析出来
									colunmObj = resultSet.getObject(columnName);
								}
								if (colunmObj != null) {
									// 根据类的属性名称得到其对应的setter方法名称 rowId ---> setRowId
									String setterMethodName = buildSetterMethodName(fieldName);
									try {
										// 根据setter方法名称得到Method的实例
										Method setterMethod = clazz.getMethod(setterMethodName, type);
										try {
											// 让方法自运行
											setterMethod.invoke(object, colunmObj);
										} catch (IllegalArgumentException | InvocationTargetException e) {
											e.printStackTrace();
										}
									} catch (NoSuchMethodException | SecurityException e) {
										e.printStackTrace();
									}
								}
							}
						}
						// 内循环执行一次后，得到一个带有数据的类的实例。将这个实例添加到集合中去。
						list.add(object);
					} catch (InstantiationException | IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {// 关闭资源
				try {
					if (resultSet != null) {
						resultSet.close();
					}
					if (statement != null) {
						statement.close();
					}
					if (connection != null) {
						connection.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}

		return list;
	}

	private static List<Field> buildAllField(Class<?> clazz) {
		List<Field> fieldList = new ArrayList<Field>();
		buildFieldData(fieldList, clazz);
		return fieldList;
	}

	private static void buildFieldData(List<Field> fieldList, Class<?> clazz) {
		Field[] fileds = clazz.getDeclaredFields();
		fieldList.addAll(Arrays.asList(fileds));
		Class<?> superClass = clazz.getSuperclass();
		if (!"java.lang.Object".equals(superClass.getName())) {
			buildFieldData(fieldList, superClass);
		}
	}

	/**
	 * # 用于执行 Insert,update,delete的SQL语句
	 * 
	 * @param sql  需要进行预编译的SQL语句
	 * @param args 可以参数，用于填充预编译SQL语句占位符的对应的值。
	 * @return 受影响的行数。
	 */
	public static int executeUpdate(String sql, Object... args) {
		Connection connection = getConnection();
		if (connection != null) {
			PreparedStatement statement = null;
			try {
				statement = connection.prepareStatement(sql);
				// 遍历通过可变参数传入的用于填充'?'占位符的值。可变参数可以传0到多个。
				if (args != null && args.length > 0) {
					int parameterIndex = 1;
					for (Object arg : args) {
						statement.setObject(parameterIndex, arg);
						parameterIndex++;
					}
				}
				// 预编译的SQL语句中的‘?’ 通过遍历可变参数的方式都已经完成数据填充了。
				return statement.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				try {
					if (statement != null) {
						statement.close();
					}
					if (connection != null) {
						connection.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return 0;
	}

	/**
	 * #根据给定的属性名称得到其对应set方法名称 rowId ---> setRowId
	 * 
	 * @param field
	 * @return
	 */
	public static String buildSetterMethodName(String field) {
		StringBuilder builder = new StringBuilder();
		char firstChar = field.charAt(0);
		builder.append("set").append(Character.toUpperCase(firstChar)).append(field.substring(1));
		return builder.toString();
	}

	/**
	 * #将类的属性名称转成 表中字段 rowId ---> ROW_ID
	 * 
	 * @param field
	 * @return
	 */
	public static String field2Column(String field) {
		StringBuilder builder = new StringBuilder();
		// 将字符串转成char的数组
		char[] charArray = field.toCharArray();
		for (char ch : charArray) {
			// 判断当前char是一个大写的
			if (Character.isUpperCase(ch)) {
				builder.append(UNDER_LINE);
			}
			// 将字符转成大写
			char upperChar = Character.toUpperCase(ch);
			builder.append(upperChar);
		}
		return builder.toString();
	}

	/**
	 * #得到数据库链接
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		Connection connection = null;
		/*
		 * try { // 加载驱动 (类加载：将类的class文件读取到JVM的内存中的过程。) Class.forName(DIVER_NAME); //
		 * 通过DriverManager得到数据库的链接 connection = DriverManager.getConnection(JDBC_URL,
		 * USER_NAME, USER_PASS); } catch (ClassNotFoundException | SQLException e) {
		 * e.printStackTrace(); }
		 */
		// 有了数据源，则应该从数据源中得到数据库的链接
		try {
			System.out.println(dataSource);
			connection = dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}
}
